package hh.visit;

import hh.Column;
import hh.EmptyUtil;
import hh.engine.Context;
import hh.engine.Table;
import hh.util.Asserts;
import hh.util.CommonUtil;
import hh.util.StringUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;

/**
 * jsp的代码会被编译成这个类的子类，所以你可以在jsp模板使用this调用此类方法或者o.调用
 */
@Setter
@Getter
@Slf4j
public abstract class TemplateWriter extends TemplateVisitor {
    /**
     * 以下public变量均可在jsp直接引用，方法需要使用o.调用，或者this.调用，但是因为this会报错，所以我在Util声明了一个空的 o字段
     * 所以，如果想要不报错，在util声明一次，不需要注入即可，方法也是一样的
     */
    public StringBuilder buffer = new StringBuilder();
    public String defaultOutputDir = CommonUtil.getClassPath(this.getClass());
    public String outputDir;
    public Table table;
    public TemplateWriter o = this; // 给jsp使用
    private String sourceCode;
    private String templateFile;
    
    Context context;
    
    volatile boolean executed = false;
    volatile boolean flushed = false;
    
    public TemplateWriter() {
    }
    
    public void write(String str) {
        if (str != null) {
            buffer.append(str);
        }
    }
    
    public void writeln(String str) {
        write(str);
        write("\n");
    }
    
    int comment = 0;
    
    public void writeComment(Column column) {
        String newline = "\n" + StringUtil.spaces(4);
        if (comment++ != 0) buffer.append("\n");
        buffer
                .append(newline).append("/**")
                .append(newline).append(" * ").append(column.columnComment)
                .append(newline).append(" */")
        ;
    }
    
    
    public void writeField(Column column) {
        String newline = "\n" + StringUtil.spaces(4);
        String s = newline + "private " + column.columnJavaType.getSimpleName() + " "
                + StringUtil.toCamelCaseField(column.columnName) + ";";
        buffer.append(s);
    }
    
    public void writeAnnotation(String annotation, String value) {
        if (!annotation.contains("@")) annotation = "@" + annotation;
        String newline = "\n" + StringUtil.spaces(4);
        String v = StringUtil.isEmpty(value) ? "" : "value = \"" + value + "\"";
        String s = newline + annotation + "(" + v + ")";
        buffer.append(s);
    }
    
    public synchronized void flushToDisk() {
        execute();
        String outputDir = getOutputDir();
        if (StringUtil.isEmpty(outputDir)) {
            String o = context.getEngine().getOutputDir();
            if (StringUtil.isEmpty(o))
                outputDir = CommonUtil.getClassPath(this.getClass()) + "/autogen";
            else outputDir = o;
        }
        if (StringUtil.containsIgnoreCase(templateFile, "service")) {
            outputDir = outputDir + "/service";
        } else if (StringUtil.containsIgnoreCase(templateFile, "dao")) {
            outputDir = outputDir + "/dao";
        } else if (StringUtil.containsIgnoreCase(templateFile, "entity")) {
            outputDir = outputDir + "/domain";
        } else if (StringUtil.containsIgnoreCase(templateFile, "controller")
                || StringUtil.containsIgnoreCase(templateFile, "api")) {
            outputDir = outputDir + "/controller";
        } else if (StringUtil.containsIgnoreCase(templateFile, "mapper")) {
            outputDir = outputDir + "/mapper";
        }
        File file1 = new File(outputDir);
        if (!file1.exists()) file1.mkdirs();
        File file = new File(outputDir, StringUtil.getClassNameFromJavaCode(buffer.toString()) + ".java");
        if (flushed) {
            log.warn("已经生成过文件请勿重复：{}", file.getAbsolutePath());
            return;
        }
        
        if (StringUtil.containsIgnoreCase(templateFile, "entity")) {
            int package_ = buffer.indexOf("package ");
            Asserts.True(package_ == -1, "jsp模板没有package关键字");
            int i = buffer.indexOf(";", package_);
            
            int import_ = buffer.indexOf("import ");
            if (import_ != -1) {
                i = import_;
            }
            buffer.insert(i, EmptyUtil.generateImportStr(table.getImportsStr()) + "\n");
        }
        
        
        System.out.println(CommonUtil.format("****-----*******-----准备输出文件：{}", file.getAbsolutePath()));
        try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(
                file), StandardCharsets.UTF_8)) {
            out.write(buffer.toString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        flushed = true;
    }
    
    public synchronized String execute() {
        if (!executed) {
            doExecute();
            executed = true;
        }
        return buffer.toString();
    }
    
    protected abstract void doExecute();
    
    
    @Override
    public void visit(Table table) {
        this.table = table;
        flushToDisk();
    }
}
